####———————— 9.3 继承
# 在编写类时，并非总是要从头开始。
# 如果要编写的类时一个既有的类的特殊版本，可使用继承（inheritance）
# 当一个类继承另一个类时，将自动获得后者的所有属性和方法。
# 原有的类称为父类（parent class），而新类称为子类（child class）
# 子类不仅继承了父类的所有属性和方法，还可以定义自己的属性和方法
import self


# 9.3.1 子类的 _init_() 方法
# 在既有的类的基础上编写新类，通常要调用父类的 _init_() 方法。
# 这将初始化在父类的 _init_() 方法中定义的所有属性，从而让子类也可以使用这些属性
# 例如，下面来模拟电动汽车， 电动汽车是一种特殊的汽车吗，因此可在之前的Car 类的基础之上
# 创建新类ElectricCar。这样只需为电动汽车特有的属性和行为编写代码即可

class Car:
    """一次模拟汽车的简单尝试"""

    def __init__(self, make, model, year):
        """初始化描述汽车的属性"""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def get_descriptive_name(self):
        """返回格式规范的描述性信息"""
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()

    def read_odometer(self):
        """打印一条指出汽车行驶里程的消息"""
        print(f"This car has {self.odometer_reading} miles on it.")

    def update_odometer(self, mileage):
        """将里程表读数设定为指定的值"""
        # self.odometer_reading = mileage
        # 还可以对此方法进行扩展，比如，禁止里程表往回调（因为里程只会越来越长）
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer! ")

    def increment_odometer(self, miles):
        """将里程表读数增加指定的量"""
        # self.odometer_reading += miles
        # 也可以扩展，禁止增加值为负值
        if miles >= 0:
            self.odometer_reading += miles
        else:
            print("You can't enter a negative value!")

# # 类的继承
# class ElectricCar(Car):
#     """电动汽车的独特之处"""
#
#     def __init__(self, make, model, year):
#         """初始化父类的属性"""
#         super().__init__(make, model, year)
#
# my_leaf = ElectricCar('nissan', 'leaf', 2024)
# print(my_leaf.get_descriptive_name())

# 在创建子类时，父类必须包含在当前文件中，且位于子类前面
# 定义子类时，必须在括号内指定父类的名称
# __init__()方法接受创建Car实例所需的信息

# super() 是一个特殊的函数，让你能够调用父类的方法

# 9.3.2 给子类定义属性和方法

# 让一个类继承另一个类之后，就可以添加区分子类和父类所需的新属性和新方法了
# 下面在子类中添加一个电动汽车的特有属性（电池），以及一个描述该属性的方法

# 类的继承
# class ElectricCar(Car):
#     """电动汽车的独特之处"""
#
#     def __init__(self, make, model, year):
#         """初始化父类的属性"""
#         super().__init__(make, model, year)
#         self.battery_size = 40 #定义子类的新属性
#
#     # 定义一个描述子类新属性的方法
#     def describe_battery(self):
#         """打印一条描述电池容量的消息"""
#         print(f"This is car has a {self.battery_size}-kwh battery.")
#
#     def fill_gas_tank(self):
#         """电车没有油箱"""
#         print("This car doesn't have a gas tank!")
#
# my_leaf = ElectricCar('nissan', 'leaf', 2024)
# print(my_leaf.get_descriptive_name())
# my_leaf.describe_battery()

# 9.3.3 重写父类中的方法
# 在使用子类模拟的实物的行为时，如果父类中的一些方法不能满足子类的需求
# 就可以用下面的办法重写
# 在子类下定义一个与父类方法重名的方法即可
# 这样Python将忽略掉父类中的这个方法。

# 9.3.4 将实例用作属性

# 在使用代码模拟实物时，你可能会发现自己给类添加了太多细节：
# 属性和方法越来越多，文件越来越长
# 在这种情况下，可能需要将类的一部分提取出来，作为一个独立的类
# 将大型类拆分为多个小类，这种方法称为组合

# 例如，在不断给ElectricCar类添加细节时，我们可能会发现其中包含很多专门针对汽车电池的属性和方法
# 在这种情况下吗，可将这些属性和方法提取出来，放到一个名为Battery的类中，并将一个Battery实例作为ECar类的属性

class Battery:
    """一次模拟电车电池的简单尝试"""
    def __init__(self, battery_size=40):
        """初始化电池的属性"""
        self.battery_size = battery_size

    def describe_battery(self):
        """打印一条描述电池容量的消息"""
        print(f"This car has a {self.battery_size}-kwh battery.")

    def get_range(self):
        """打印一条消息，指出电池的续航里程"""
        if self.battery_size == 40:
            range = 150
        elif self.battery_size == 65:
            range = 225
        print(f"This car can go about {range} miles on a full charge.")

class ElectricCar(Car):
    """电车的独特之处"""

    def __init__(self, make, model, year):
        """先初始化父类的属性，在初始化电车的特有属性"""
        super().__init__(make, model, year)
        #注意
        #self.battery = Battery
        # 错误：将 Battery 类本身赋值给 self.battery
        # 你需要将 Battery 类的实例赋值给 self.battery，而不是 Battery 类本身。

        self.battery = Battery()  # 将小类作为新子类的属性

my_leaf = ElectricCar('nissan', 'leaf', 2024)
print(my_leaf.get_descriptive_name())
my_leaf.battery.describe_battery()
my_leaf.battery.get_range()

# 9.3.5 模拟实物

# 在模拟较复杂的事物（如电动汽车）时，需要思考一些有趣的问题。
# 续航里程是电池的属性，还是汽车的属性呢？
# 如果只描述一辆汽车， 将 get_range() 方法放在Battery类中也许是合适的
# 但如果要描述一整个生产线，最好将 get_range() 转移到 ElectricCar 中
# 在这种情况下,get_range() 依然根据电池容量来确定续航里程，但报告的是一款汽车，而非一辆汽车
# 也可以将 get_range() 留在Battery类中，但向他传递一个参数，如 car_model
# 此时，get_range() 方法将根据电池容量和汽车型号报告续航里程

# 这让你进入了程序员的另一个境界： 在解决上述问题时，从较高的逻辑层面（而不是语法层面）思考
# 你考虑的不是Python，而是如何使用代码来表示实际的事物




















































